package cn.smile.commons.exceptions;

import cn.smile.business.msg.IEmailInfoService;
import cn.smile.commons.constant.MailConstant;
import cn.smile.commons.enums.msg.MailCodeEnum;
import cn.smile.commons.response.MyResponseCode;
import cn.smile.commons.response.MyResult;
import cn.smile.utils.DateFormatUtil;
import com.google.common.collect.Maps;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 全局业务异常处理
 * </>
 *
 * @author 龙逸
 * @since: 2020-09-15 21:10:17
 **/
@Slf4j
@ControllerAdvice
public class BusinessExceptionHandler {
    @Resource
    private IEmailInfoService emailService;
    @Value("${email.sendError}")
    private Boolean sendErrorFlag;

    @ExceptionHandler({BusinessException.class, Exception.class})
    public ResponseEntity<?> handlerException(Exception ex) {
        log.error("Handler Exception ex: ", ex);
        MyResult<String> result = new MyResult<>();
        // 业务异常
        if (ex instanceof BusinessException) {
            result.setRetCode(((BusinessException) ex).getCode());
            result.setMessage(ex.getMessage());
            log.warn("[全局业务异常]\r业务编码：{}\r异常记录：{}", result.getRetCode(), result.getMessage());
        }
        //请求参数对象
        else if (ex instanceof HttpMessageNotReadableException) {
            result.setRetCode(MyResponseCode.UNDEFINED.code());
            result.setMessage(MyResponseCode.UNDEFINED.message());
        }
        //请求参数对象
        else if (ex instanceof HttpRequestMethodNotSupportedException) {
            result.setRetCode(MyResponseCode.REQ_METHOD_ERROR.code());
            result.setMessage(MyResponseCode.REQ_METHOD_ERROR.message());
        }
        // 统一处理 JSON 参数验证
        else if (ex instanceof MethodArgumentNotValidException methodArgumentNotValidException) {
            BindingResult bindingResult = methodArgumentNotValidException.getBindingResult();
            String msg = bindingResult.getFieldErrors().stream().map(FieldError::getDefaultMessage).distinct().collect(Collectors.joining(","));
            result.setRetCode(HttpStatus.BAD_REQUEST.value());
            result.setMessage(msg);
        }
        // 统一处理表单绑定验证
        else if (ex instanceof BindException bindException) {
            BindingResult bindingResult = bindException.getBindingResult();
            String msg = bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).distinct().collect(Collectors.joining(","));
            result.setRetCode(HttpStatus.BAD_REQUEST.value());
            result.setMessage(msg);
        }
        // 未知错误
        else {
            result.setRetCode(MyResponseCode.UNKNOWN.code());
            result.setMessage(MyResponseCode.UNKNOWN.message());
            log.error(ex.getMessage());
            if (sendErrorFlag) {
                this.sendEmail(ex);
            }
        }
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    /**
     * 捕捉404异常
     *
     * @param e 异常信息
     * @return 返回结果
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<?> handle404(NoHandlerFoundException e) {
        log.error(e.getMessage());
        MyResult<String> result = new MyResult<>();
        result.setRetCode(MyResponseCode.NOT_FOUND.code());
        result.setMessage(MyResponseCode.NOT_FOUND.message());
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    /**
     * 发送错误信息邮件
     *
     * @param e 错误信息
     */
    private void sendEmail(Exception e) {
        Map<String, String> params = Maps.newHashMap();
        params.put("errorMsg", this.getErrorString(e));
        params.put("errorTime", DateFormatUtil.parseDateToStr(new Date(), DateFormatUtil.DATE_TIME_FORMAT_1));
        params.put("receiveName", MailConstant.ADMIN_USER_NAME);
        emailService.sendMail(MailCodeEnum.SYSTEM_ERROR, params, MailConstant.ADMIN_USER_EMAIL);
    }

    /**
     * 错误信息转String
     *
     * @param e 错误信息
     * @return 转换后的String
     */
    public String getErrorString(Exception e) {
        try {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            return sw.toString();
        } catch (Exception e2) {
            return "处理异常信息失败!";
        }
    }
}
